
<template>
  <div>state:{{ state }}</div>
  <div>undoStackPointer:{{ undoStackPointer }}</div>

  <div class="set">
    <button @click="onAdd">Add</button>
    <button @click="onUndo">undo</button>
    <button @click="onRedo">redo</button>
  </div>

  <main>
    <article>
      <div class="block" v-for="block in state" :key="block.id">
        <input type="text" :value="block.text" @keydown.enter="onEnter(block, $event)">
        <button @click="onRemove(block)">remove</button>
      </div>
    </article>
  
    <div class="history">
      <ul>
        <li v-for="(item, index) in undoStack" :key="index" @click="onHisClick(index)">
          <div class="header">
            <small>{{ index +1 }}. </small>
            <span>{{ item.action }}</span>
            <i v-if="index === undoStackPointer">(active)</i>
          </div>
          <!-- <div class="body">
            <br>patches: {{ item.patches }}
            <br>iversePatches: {{ item.inversePatches }}
          </div> -->
        </li>
      </ul>
    </div>
  </main>
</template>

<script setup>
import { ref, shallowRef } from 'vue'
import { produceWithPatches, enablePatches, applyPatches } from 'immer'
import { nanoid } from 'nanoid'

enablePatches()
// 历史记录页面板
const undoStack = ref([])
const undoStackPointer = ref(-1)
const state = shallowRef([])


const nextState = ref([])
const patches = ref([])
const inversePatches = ref([])

/**
 * 更新数据
 * @param {function} updater 更新方法
 * @param {string} action 更新名称
 */
function update(action, updater) {
  const [nextState, patches, inversePatches] = produceWithPatches(state.value, updater)

  state.value = nextState
  ++undoStackPointer.value
  // nextState.value = updateData[0]
  // patches.value = updateData[1]
  // inversePatches.value = updateData[2]
  undoStack.value.length = undoStackPointer.value
  undoStack.value.push({
    action,
    patches,
    inversePatches
  })
}

function onAdd(e) {
  console.log(e.target.value)
  update('Add Block', draft => {
    draft.push({
      id: nanoid(),
      text: '',
      color: '#333'
    })
  })
}

function onEnter(block, e) {
  console.log(block, e)
  update('update text', draft => {
    const index = draft.findIndex(item => item.id === block.id)
    draft[index].text = e.target.value
  })
}

function onRemove(block) {
  update('remove block', draft => {
    const index = draft.findIndex(item => item.id === block.id)
    draft.splice(index, 1)
  })
}

function onUndo() {
  if (undoStackPointer.value < 0) return
  const patches = undoStack.value[undoStackPointer.value].inversePatches
  state.value = applyPatches(state.value, patches)
  undoStackPointer.value--
}

function onRedo() {
  if (undoStackPointer.value >= undoStack.value.length - 1) return
  undoStackPointer.value++
  const patches =undoStack.value[undoStackPointer.value].patches
  state.value = applyPatches(state.value, patches)
}

function onHisClick(index) {
  let res = []
  if (index === undoStackPointer.value) {
    return
  } else if (index < undoStackPointer.value) {
    for (let i = index + 1; i <= undoStackPointer.value; i++) {
      const element = undoStack.value[i];
      res.unshift(...element.inversePatches)
    }
  } else {
    for (let i = undoStackPointer.value + 1; i <= index; i++) {
      const element = undoStack.value[i];
      res.push(...element.patches)
    }
  }

  state.value = applyPatches(state.value, res)
  undoStackPointer.value = index
}

// console.log(nextState)
// console.log(changes)
// console.log(inversePatches)
</script>


<style lang="less" src="./style.less" scoped></style>
